package com.cloudcentral.service;

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.cloudcentral.entity.Platform;
import com.cloudcentral.entity.User;
import com.cloudcentral.entity.UserAudit;
import com.cloudcentral.entity.UserRole;
import com.cloudcentral.entity.dto.*;
import com.cloudcentral.entity.query.UserQuery;
import com.cloudcentral.entity.vo.PageVO;
import com.cloudcentral.entity.vo.UserPageVO;
import com.cloudcentral.exception.FailureException;
import com.cloudcentral.exception.InvalidException;
import com.cloudcentral.feign.ToolkitFeign;
import com.cloudcentral.mapper.PlatformBaseMapper;
import com.cloudcentral.mapper.UserAuditMapper;
import com.cloudcentral.mapper.UserMapper;
import com.cloudcentral.mapper.UserRoleBaseMapper;
import com.cloudcentral.util.DateUtils;
import com.cloudcentral.util.PatternUtils;
import com.cloudcentral.util.VerifyUtils;
import com.cloudcentral.util.WebUtils;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Calendar;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 系统用户表 服务实现类
 * </p>
 *
 * @author Administrator
 * @since 2023-10-27
 */
@Slf4j
@Service
public class UserService extends ServiceImpl<UserMapper, User> {
  private final UserRoleBaseMapper userRoleMapper;
  private final UserAuditMapper userAuditMapper;
  private final PlatformBaseMapper platformBaseMapper;
  private final RedissonClient redissonClient;
  private final PasswordEncoder passwordEncoder;
  private final ToolkitFeign toolkitFeign;

  @Autowired
  public UserService(UserRoleBaseMapper userRoleMapper, UserAuditMapper userAuditMapper, PlatformBaseMapper platformBaseMapper, RedissonClient redissonClient, PasswordEncoder passwordEncoder, ToolkitFeign toolkitFeign) {
    this.userRoleMapper = userRoleMapper;
    this.userAuditMapper = userAuditMapper;
    this.platformBaseMapper = platformBaseMapper;
    this.redissonClient = redissonClient;
    this.passwordEncoder = passwordEncoder;
    this.toolkitFeign = toolkitFeign;
  }

  public User getUser(String name) {
    return baseMapper.selectUser(name);
  }

  public PageVO<UserPageVO> listPage(UserQuery query) {
    IPage<UserPageVO> iPage = baseMapper.listPage(new Page<>(query.getPage(), query.getSize()), query);
    return new PageVO<>(iPage.getRecords(), iPage.getTotal());
  }

  @Transactional(rollbackFor = {Exception.class})
  @GlobalTransactional(rollbackFor = {Exception.class})
  public Boolean audit(UserAuditDTO dto, Long userId) throws Exception {
    if (dto.getPass() == null) throw new FailureException("缺少审核结果");
    else {
      userAuditMapper.deleteById(dto.getId());
      if (dto.getPass()) {
        if (dto.getPlatformId() == null) throw new FailureException("缺少平台ID");
        else if (dto.getRoleId() == null) throw new FailureException("缺少角色ID");
        else if (dto.getRegisterTime() == null) throw new FailureException("缺少注册时间");
        else if (StringUtils.isBlank(dto.getPhone())) throw new FailureException("缺少手机号");
        else if (StringUtils.isBlank(dto.getUsername())) throw new FailureException("缺少用户名");
        else if (StringUtils.isBlank(dto.getPassword())) throw new FailureException("缺少密码");
        else {
          User user = User.builder()
              .parentId(userId)
              .platformId(dto.getPlatformId())
              .phone(dto.getPhone())
              .username(dto.getUsername())
              .password(dto.getPassword())
              .avatar(dto.getAvatar())
              .gender(dto.getGender())
              .age(dto.getAge())
              .remark(dto.getRemark())
              .type(1)
              .registerTime(dto.getRegisterTime())
              .build();
          save(user);
          userRoleMapper.insert(UserRole.builder().userId(user.getId()).roleId(dto.getRoleId()).build());
          return true;
        }
      } else return false;
    }
  }

  @Transactional(rollbackFor = {Exception.class})
  @GlobalTransactional(rollbackFor = {Exception.class})
  public Boolean enable(EnableDTO dto) throws FailureException {
    if (dto.getId() == null) throw new FailureException("缺少ID");
    else if (dto.getEnable() == null) throw new FailureException("缺少启用状态");
    else {
      UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
      updateWrapper.set("enable", dto.getEnable() ? 1 : 0).eq("id", dto.getId());
      return update(updateWrapper);
    }
  }

  @Transactional(rollbackFor = {Exception.class})
  @GlobalTransactional(rollbackFor = {Exception.class})
  public Boolean submit(UserDTO dto, Long userId) throws Exception {
    if (dto.getPlatformId() == null) throw new FailureException("缺少平台ID");
    else if (dto.getRoleId() == null) throw new FailureException("缺少角色ID");
    else if (StringUtils.isBlank(dto.getPhone())) throw new FailureException("缺少手机号");
    else if (StringUtils.isBlank(dto.getUsername())) throw new FailureException("缺少用户名");
    else if (dto.getId() == null) {
      if (StringUtils.isBlank(dto.getPassword())) throw new FailureException("缺少密码");
      else {
        Date date = Calendar.getInstance().getTime();
        User user = User.builder()
            .parentId(userId)
            .platformId(dto.getPlatformId())
            .phone(dto.getPhone())
            .username(dto.getUsername())
            .password(passwordEncoder.encode(dto.getPassword()))
            .avatar(dto.getAvatar())
            .gender(dto.getGender())
            .age(dto.getAge())
            .remark(dto.getRemark())
            .type(0)
            .registerTime(date)
            .build();
        save(user);
        userRoleMapper.insert(UserRole.builder().userId(user.getId()).roleId(dto.getRoleId()).build());
        return true;
      }
    } else {
      User user = User.builder()
          .id(dto.getId())
          .platformId(dto.getPlatformId())
          .phone(dto.getPhone())
          .username(dto.getUsername())
          .avatar(dto.getAvatar())
          .gender(dto.getGender())
          .age(dto.getAge())
          .remark(dto.getRemark())
          .build();
      updateById(user);
      UpdateWrapper<UserRole> updateWrapper = new UpdateWrapper<>();
      updateWrapper.eq("user_id", dto.getId());
      userRoleMapper.update(UserRole.builder().userId(dto.getId()).roleId(dto.getRoleId()).build(), updateWrapper);
      return true;
    }
  }

  @Transactional(rollbackFor = {Exception.class})
  @GlobalTransactional(rollbackFor = {Exception.class})
  public Boolean delete(Long id) throws Exception {
    User user = getById(id);
    removeById(id);
    toolkitFeign.delete(user.getAvatar());
    return true;
  }

  @Transactional(rollbackFor = {Exception.class})
  @GlobalTransactional(rollbackFor = {Exception.class})
  public Boolean resetPwd(ResetPwdDTO dto) throws Exception {
    if (StringUtils.isBlank(dto.getPassword())) throw new FailureException("缺少密码");
    else if (StringUtils.isBlank(dto.getCode())) throw new FailureException("缺少短信验证码");
    else if (!PatternUtils.isPassword(dto.getPassword())) throw new FailureException("密码只允许字母、数字、下划线，长度限制6-18位");
    else {
      RLock lock = redissonClient.getLock("resetPwd");
      try {
        lock.tryLock(10, 10, TimeUnit.SECONDS);
        String name = WebUtils.currentName();
        if (StringUtils.isBlank(name)) throw new InvalidException();
        else {
          User user = getUser(name);
          VerifyUtils.code(redissonClient, user.getPhone(), dto.getCode());
          UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
          updateWrapper.set("password", dto.getPassword()).eq("id", user.getId());
          return updateById(User.builder()
              .id(user.getId())
              .password(passwordEncoder.encode(dto.getPassword()))
              .build());
        }
      } finally {
        lock.unlock();
      }
    }
  }

  @Transactional(rollbackFor = {Exception.class})
  @GlobalTransactional(rollbackFor = {Exception.class})
  public Boolean register(RegisterDTO dto) throws Exception {
    if (StringUtils.isBlank(dto.getPhone())) throw new FailureException("缺少手机号");
    else if (StringUtils.isBlank(dto.getUsername())) throw new FailureException("缺少用户名");
    else if (StringUtils.isBlank(dto.getPassword())) throw new FailureException("缺少密码");
    else if (StringUtils.isBlank(dto.getCode())) throw new FailureException("缺少短信验证码");
    else if (StringUtils.isBlank(dto.getPlatformCode())) throw new FailureException("缺少平台代码");
    else if (!PatternUtils.isPhone(dto.getPhone())) throw new FailureException("手机号格式错误");
    else if (!PatternUtils.isUsername(dto.getUsername())) throw new FailureException("用户名只允许中文或英文");
    else if (!PatternUtils.isPassword(dto.getPassword())) throw new FailureException("密码只允许字母、数字、下划线，长度限制6-18位");
    else {
      VerifyUtils.code(redissonClient, dto.getPhone(), dto.getCode());
      Platform platform = platformBaseMapper.getByCode(dto.getPlatformCode());
      if (platform == null) throw new FailureException("平台不存在");
      else if (!platform.getEnable()) throw new FailureException("平台已冻结");
      else if (getUser(dto.getUsername()) != null || userAuditMapper.selectUser(dto.getUsername()) != null) throw new FailureException("用户名已注册");
      else if (getUser(dto.getPhone()) != null || userAuditMapper.selectUser(dto.getPhone()) != null) throw new FailureException("手机号已注册");
      else {
        RLock lock = redissonClient.getLock("register");
        try {
          lock.tryLock(10, 10, TimeUnit.SECONDS);
          UserAudit userAudit = UserAudit.builder()
              .platformId(platform.getId())
              .phone(dto.getPhone())
              .username(dto.getUsername())
              .password(passwordEncoder.encode(dto.getPassword()))
              .build();
          log.info("用户({})注册到平台({})，时间: {}", userAudit.getUsername(), platform.getName(), DateUtils.now());
          return SqlHelper.retBool(userAuditMapper.insert(userAudit));
        } finally {
          lock.unlock();
        }
      }
    }
  }
}
